/*
 * ServidorRemoto.cpp
 *
 *  Created on: 22/07/2014
 *      Author: migue
 */

#include "ServidorRemoto.h"
#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>

#include <rpc/clnt.h>
#include <memory.h> /* for memset */
#include "../ServidorRPC/servidor_rpc.h"

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

RespuestaRPC *
identificar_1(DatosEntidad *argp, CLIENT *clnt)
{
	static RespuestaRPC clnt_res;

	memset((char *)&clnt_res, 0, sizeof(clnt_res));
	if (clnt_call (clnt, identificar,
		(xdrproc_t) xdr_DatosEntidad, (caddr_t) argp,
		(xdrproc_t) xdr_RespuestaRPC, (caddr_t) &clnt_res,
		TIMEOUT) != RPC_SUCCESS) {
		return (NULL);
	}
	return (&clnt_res);
}

/********************************************************************/

ServidorRemoto::ServidorRemoto() {
	_resp = NULL;
	_datosEnt.pid = getpid();
}

ServidorRemoto::~ServidorRemoto() {
}

void ServidorRemoto::consultar_servidor()
{

	_datosEnt.pid = getpid();

	CLIENT *clnt;

#ifndef	DEBUG
	clnt = clnt_create (NOMBRE_SERVIDOR_IDS, SERVPROG, SERVVERS, "udp");
	if (clnt == NULL) {
		clnt_pcreateerror (NOMBRE_SERVIDOR_IDS);
		exit (1);
	}
#endif	/* DEBUG */

	_resp = identificar_1(&_datosEnt, clnt);
	if (_resp == (RespuestaRPC *) NULL) {
		clnt_perror (clnt, "call failed");
	}
#ifndef	DEBUG
	clnt_destroy (clnt);
#endif	 /* DEBUG */


	if (_resp != NULL) {
		if (_resp->RespuestaRPC_u.datos.id_entidad == -8888) {
			usleep(100);
			consultar_servidor();
		}
	}
}


int ServidorRemoto::consultarDatos(int id) {
	_datosEnt.numeroEntidad = 0;
	_datosEnt.entidad = TIPO_DESCONOCIDO;
	_datosEnt.id_auxiliar = id;
	_datosEnt.metodo = MTD_CONSULTAR_DATOS;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo consultarDatos");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdAdministrador() {
	_datosEnt.numeroEntidad = 0;
	_datosEnt.id_auxiliar = 0;
	_datosEnt.entidad = TIPO_ADMIN;
	_datosEnt.metodo = MTD_OBTENER_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdAdmin");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdPersona(int idPropio) {
	syslog(LOG_INFO,"Peticion de id de Persona, id local: %i",idPropio);
	_datosEnt.numeroEntidad = 0;
	_datosEnt.id_auxiliar = idPropio;
	_datosEnt.entidad = TIPO_PERSONA;
	_datosEnt.metodo = MTD_OBTENER_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdPersona");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdSala(int numSala, Tipos::TipoSala tipo, Tipos::LugarDeSala lugar) {
	syslog(LOG_INFO,"Peticion de id Sala con numSala: %i, tipo: %i, lugar: %i", numSala, tipo, lugar);

	_datosEnt.numeroEntidad = numSala;

	if (tipo == Tipos::TP_ENTRADA_COLA) {
		_datosEnt.entidad = TIPO_SALA_COLA;
	}
	else if (tipo == Tipos::TP_ENTRADA_SALA) {
		_datosEnt.entidad = TIPO_SALA_ENT;
	}
	else if (tipo == Tipos::TP_SALIDA) {
		_datosEnt.entidad = TIPO_SALA_SAL;
	}
	else {
		// TODO loguear error
		perror("Retornando ID de error en metodo pedirIdSala");
		return ID_ERROR;
	}


	_datosEnt.lugar = (LugarParque) lugar;

	_datosEnt.metodo = MTD_OBTENER_ID;


	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdSala");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdPuerta(int numPuerta, Tipos::TipoDePuerta tipo) {
	syslog(LOG_INFO,"Peticion de id de Puerta numPuerta: %i, tipo: %i", numPuerta, tipo);
	_datosEnt.numeroEntidad = numPuerta;

	if (tipo == Tipos::PUERTA_ENTRADA) {
		_datosEnt.entidad = TIPO_PUERTA_ENT;
	}
	else if (tipo == Tipos::PUERTA_SALIDA) {
		_datosEnt.entidad = TIPO_PUERTA_SAL;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdPuerta");
		return ID_ERROR;
	}

	_datosEnt.metodo = MTD_OBTENER_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdPuerta");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdBus(int numBus) {
	//syslog(LOG_INFO,"Peticion de id BUS con numSala: %i, tipo: %i, lugar: %i", numSala, tipo, lugar);

	_datosEnt.numeroEntidad = numBus;
	_datosEnt.entidad = TIPO_BUS;
	_datosEnt.metodo = MTD_OBTENER_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdBus");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdBUSRepuestoGeneral(int& idRepuestoGral) {
	_datosEnt.numeroEntidad = -1;
	_datosEnt.entidad = TIPO_BUS;
	_datosEnt.metodo = MTD_ID_BUS_REEMPLAZO;

	consultar_servidor();

	idRepuestoGral = _resp->RespuestaRPC_u.datos.id_auxiliar;

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo PEDIR_IdBusRepuestoGeneral");
		return ID_ERROR;
	}
}

int ServidorRemoto::idBusRepuestoGeneral() {
	_datosEnt.numeroEntidad = 0;
	_datosEnt.entidad = TIPO_BUS;
	_datosEnt.metodo = MTD_ID_BUS_REEMPLAZO;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_auxiliar;
	}
	else {
		perror("Retornando ID de error en metodo idBusRepuestoGeneral");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdBUSRepuesto(int numBusRoto, int idBusRepuesto) {
	_datosEnt.numeroEntidad = numBusRoto;
	_datosEnt.id_auxiliar = idBusRepuesto;

	_datosEnt.entidad = TIPO_BUS;
	_datosEnt.metodo = MTD_REEMPLAZAR_BUS;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdBusRepusto");
		return ID_ERROR;
	}
}

int ServidorRemoto::pedirIdBroker(int numBroker, int puertoEscucha) {

	_datosEnt.numeroEntidad = numBroker;
	_datosEnt.entidad = TIPO_BROKER;

	_datosEnt.puerto_escucha = puertoEscucha;

	_datosEnt.metodo = MTD_OBTENER_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo pedirIdBroker");
		return ID_ERROR;
	}
}

void ServidorRemoto::datosConexion(int& idBroker, struct sockaddr& dirBroker, int& puertoBroker) {
	if (_resp == NULL) {
		idBroker = ID_ERROR;
		memset(&dirBroker, 0, sizeof(struct sockaddr));
		puertoBroker = 0;
	}
	else if (_resp->error == 0) {
		idBroker = _resp->RespuestaRPC_u.datos.id_brokerConectado;
		memcpy(&dirBroker, _resp->RespuestaRPC_u.datos.dir_conexion._dir, sizeof(struct sockaddr));
		puertoBroker = _resp->RespuestaRPC_u.datos.puerto_conexion;
	}
	else {
		idBroker = ID_ERROR;
		memset(&dirBroker, 0, sizeof(struct sockaddr));
		puertoBroker = 0;
	}
}

void ServidorRemoto::datosBroker(int& timeoutAnillo) {
	if (_resp == NULL) {
		timeoutAnillo = ID_ERROR;
	}
	else if (_resp->error == 0) {
		timeoutAnillo =  _resp->RespuestaRPC_u.datos.timeout_anillo;
	}
	else {
		perror("Retornando ID de error en metodo datosBroker");
		timeoutAnillo = ID_ERROR;
	}
}

int ServidorRemoto::idBrokerConectado() {
	if (_resp == NULL) {
		return ID_ERROR;
	}
	else if (_resp->error == 0) {
		return  _resp->RespuestaRPC_u.datos.id_brokerConectado;
	}
	else {
		perror("Retornando ID de error en metodo idBrokerConectado");
		return ID_ERROR;
	}
}

int ServidorRemoto::consultarIdSala(int numSala, Tipos::TipoSala tipo, Tipos::LugarDeSala lugar) {
	_datosEnt.numeroEntidad = numSala;

	if (tipo == Tipos::TP_ENTRADA_COLA) {
		_datosEnt.entidad = TIPO_SALA_COLA;
	}
	else if (tipo == Tipos::TP_ENTRADA_SALA) {
		_datosEnt.entidad = TIPO_SALA_ENT;
	}
	else if (tipo == Tipos::TP_SALIDA) {
		_datosEnt.entidad = TIPO_SALA_SAL;
	}
	else {
		// TODO loguear error
		perror("Retornando ID de error en metodo consultarIdSala");
		return ID_ERROR;
	}


	_datosEnt.lugar = (LugarParque) lugar;

	_datosEnt.metodo = MTD_CONSULTAR_ID;


	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo consultarIdSala");
		return ID_ERROR;
	}
}


int ServidorRemoto::consultarIdPuerta(int numPuerta, Tipos::TipoDePuerta tipo) {
	_datosEnt.numeroEntidad = numPuerta;

	if (tipo == Tipos::PUERTA_ENTRADA) {
		_datosEnt.entidad = TIPO_PUERTA_ENT;
	}
	else if (tipo == Tipos::PUERTA_SALIDA) {
		_datosEnt.entidad = TIPO_PUERTA_SAL;
	}
	else {
		perror("Retornando ID de error en metodo consultarIdPuerta");
		return ID_ERROR;
	}

	_datosEnt.metodo = MTD_CONSULTAR_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo consultarIdPuerta");
		return ID_ERROR;
	}
}

int ServidorRemoto::consultarIdBus(int numBus) {
	_datosEnt.numeroEntidad = numBus;
	_datosEnt.entidad = TIPO_BUS;
	_datosEnt.metodo = MTD_CONSULTAR_ID;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_entidad;
	}
	else {
		perror("Retornando ID de error en metodo consultarIdBus");
		return ID_ERROR;
	}
}


int ServidorRemoto::informarBrokerCaido(int idCliente, int idBrokerCaido) {
	_datosEnt.numeroEntidad = idCliente;
	_datosEnt.entidad = TIPO_DESCONOCIDO;
	_datosEnt.id_auxiliar = idBrokerCaido;
	_datosEnt.metodo = MTD_BROKER_CAIDO;

	consultar_servidor();

	if (_resp != NULL && _resp->error == 0){
		return _resp->RespuestaRPC_u.datos.id_brokerConectado;
	}
	else {
		perror("Retornando ID de error en metodo consultarIdBus");
		return ID_ERROR;
	}
}

void ServidorRemoto::datosDeBroker(int idBroker, struct sockaddr& dirBroker, int& puertoEscucha) {
	int aux;
	this->consultarDatos(idBroker);
	this->datosConexion(aux, dirBroker, puertoEscucha);
}


RespuestaRPC* ServidorRemoto::respuesta() {
	return _resp;
}
